iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 23
0

文同步分享於個人blog

  • 定義

The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.

翻譯年糕:將一個請求封裝成一個物件,讓你可用不同的請求對客戶進行參數化、對請求排隊或記錄請求日誌,以及支援可取消的操作

這是什麼意思?當A要請求B執行任務時,A會呼叫B然後B在完成任務,在這種情況下A需直接和B進行溝通,就像A是老闆,他要交代員工B去做事情一樣,如下圖。我們稱做A為Sender,B為Reciver。

c1

但如果Sander需要Reciver做很多事怎麼辦?就像是老闆非常忙碌,他沒辦法提醒每個人該做什麼事情,那該怎麼辦?這時老闆可以使用備忘錄。他將任務寫在備忘錄上,再經由秘書把任務交給員工完成。

c2

老闆(Sender)將任務封裝成備忘錄(Command),然後秘書(Invoker)再經由備忘錄的工作事項分派任務給員工(Receiver)。如此一來老闆不需要知道是哪個員工執行,只需要秘書回報任務結果即可。看完這兩張簡易的流程圖,我們對於Command Pattern有了初步的了解。

  • Command Pattern 成員

Command Pattern有幾個組成角色

成員 功用
Command 用來宣告執行操作的interface / abstract class。
ConcreteCommand Command的實體物件,通常會持有Receiver,並呼叫Receiver的功能來完成命令要執行的操作。
Receiver(接收者) 幹活的角色, 命令傳遞到被執行。
Invoker(請求者) 接收並要求執行命令。
Client(裝配者) 建立Command Object,組裝Command Object和Receiver

註:這邊的Clinet並非客戶端的Client,解釋為裝配者較適合,因為使用命令的Client應該是從Invoker來觸發執行

我們可以用下面的UML圖來了解個角色的位置

c3

由表格對應UML可得知,Invoker為呼叫者,負責要求執行命令;Command為命令的介面或抽象類;ConcreteCommand為各種命令實體繼承Command;Receiver為執行者負責執行命令;Client內定義接收者、命令以及呼叫者,接著將命令交給呼叫者執行。

Command Pattern 實作


認識完Command Pattern的角色後,我們將這些角色套用在程式碼內試試看:

Receiver:負責執行命令

class Receiver {
    public void action(String str) {
        System.out.println(str);
    }
}

Command:宣告執行命令的abstract class

abstract class Command{
    Receiver receiver;

    public Command(Receiver receiver){
        this.receiver = receiver;
    }
    public abstract void execute();
}

ConcreteCommand:Command的實體物件,持有並呼叫Receiver的功能來完成命令要執行的操作

class ConcreteCommand extends Command{
    ConcreteCommand(Receiver receiver){
        super(receiver);
    }

    @Override
    public void execute(){
        receiver.action();
    }
}

Invoker:接收並要求執行命令

class Invoker {
    private List<Command> commandList = new ArrayList<>();
    public void setCommand(Command command){
        commandList.add(command);
    }
    public void executeCommand(){
        System.out.println("Invoker call Receiver");
        for (Command command :
                commandList) {
            command.execute();
        }
    }
}

Client

public class CommandPattern {
    public static void main(String[] args) {
        // 定義Receiver
        Receiver receiver = new Receiver();
        // 定義給Invoker的Command(可以多個)
        Command command = new ConcreteCommand(receiver);
        //定義Invoker 
        Invoker invoker = new Invoker();

        System.out.println("Client call Invoker ...");
        // Invoker接收指令
        invoker.setCommand(command);
        // Invoker執行指令
        invoker.executeCommand();
    }
}

output

Client call Invoker ...
Invoker call Receiver
Receiver do action

由上述程式碼,應該更加了解Command Pattern執行的流程,如果還對流程有些模糊,可以參考下面時序圖的流程。

c4


現在我們了解也學會了如何使用Command Pattern,接著就回到老闆及員工的範例,我們來請秘書分擔老闆的事情:

class Employee {
    public void action(String str) {
        System.out.println(str);
    }
}

abstract class Command{
    Employee employee;

    public Command(Employee employee){
        this.employee = employee;
    }
    public abstract void execute();
}

class MeetingCommand extends Command{
    MeetingCommand(Employee employee){
        super(employee);
    }

    @Override
    public void execute(){
        employee.action("Employee do MeetingCommand");
    }
}

class PrepareSlideCommand extends Command{
    PrepareSlideCommand(Employee employee){
        super(employee);
    }

    @Override
    public void execute(){
        employee.action("Employee do PrepareSlideCommand");
    }
}

class Secretary {
    private List<Command> commandList = new ArrayList<>();
    public void setCommand(Command command){
        commandList.add(command);
    }
    public void executeCommand(){
        System.out.println("Secretary call Receiver");
        for (Command command :
                commandList) {
            command.execute();
        }
    }
}

public class CommandPattern {
    public static void main(String[] args) {
        // 定義Receiver
        Employee employee = new Employee();
        // 定義給Invoker的Command(可以多個)
        Command prepareSlide=new PrepareSlideCommand(employee);
        Command meeting=new MeetingCommand(employee);
        //定義Invoker
        Secretary secretary=new Secretary();

        System.out.println("Boss call Secretary  ...");
        // Invoker接收指令
        secretary.setCommand(prepareSlide);
        secretary.setCommand(meeting);
        // Invoker執行指令
        secretary.executeCommand();
    }
}

output

Boss call Secretary  ...
Secretary call Receiver
Employee do PrepareSlideCommand
Employee do MeetingCommand

如此一來老闆就不需要盯著每一個員工做事,他只需要將事情交代給秘書,由秘書去叫員工把事情做好即可。

  • 小結


Command Pattern 的優缺點
優點 缺點
- 降低耦合度 - 易擴充 - 容易設計組合命令 - 若系統需要非常大量的Command,會影像到Command Pattern的使用
Command Pattern 的組成
  • Command: 用來宣告執行操作的interface / abstract class。
  • ConcreteCommand: Command的實體物件,持有並呼叫Receiver執行的操作。
  • Receiver: 執行命令。
  • Invoker: 接收並要求執行命令。
  • Client: 建立Command Object,組裝Command Object和Receiver
Command Pattern 的目標

請求以命令的形式封裝在物件中,並傳給調用對象。調用對象尋找可處理該命令的適合對象,並將命令傳給合適的對象執行。

  • 範例程式碼


範例:Command Pattern 實作

  • References


初探設計模式 - 命令模式 ( Command Pattern )
命令模式
簡說設計模式——命令模式


上一篇
[Day22] 責任鏈模式 | Chain of Responsibility Pattern
下一篇
[Day24] 迭代器模式 | Iterator Pattern
系列文
從生活中認識Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言